<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>高科技抽奖转盘 — 下周技术分享抽签</title>
<style>
  :root{
    --bg:#05060a;
    --panel:#0b1220b0;
    --neon1:#00e5ff;
    --neon2:#8a2be2;
    --accent:#00ffd5;
    --glass: rgba(255,255,255,0.06);
  }
  html,body{height:100%;margin:0;font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial;}
  body{
    background: radial-gradient(1200px 600px at 10% 10%, rgba(138,43,226,0.08), transparent),
                radial-gradient(900px 500px at 95% 90%, rgba(0,229,255,0.06), transparent),
                var(--bg);
    color:#e6f7ff;
    -webkit-font-smoothing:antialiased;
    display:flex;align-items:center;justify-content:center;
    overflow:hidden;
  }

  /* Main container */
  .wrap{
    width:1000px; max-width:96vw; height:640px; max-height:90vh;
    display:grid; grid-template-columns: 520px 1fr; gap:20px;
    align-items:center; justify-items:center;
    padding:28px; box-sizing:border-box;
  }

  /* Left: wheel */
  .wheel-card{
    width:520px;height:520px;border-radius:28px;
    background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.00));
    box-shadow: 0 12px 40px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.02);
    display:flex;align-items:center;justify-content:center;position:relative;
    border:1px solid rgba(255,255,255,0.04);
    backdrop-filter: blur(8px);
  }

  /* center lens */
  .center-lens{
    position:absolute;width:220px;height:220px;border-radius:50%;
    background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.06), rgba(255,255,255,0.01));
    display:flex;align-items:center;justify-content:center;flex-direction:column;
    transform: translateZ(40px);
    box-shadow: 0 8px 40px rgba(138,43,226,0.08), 0 2px 8px rgba(0,0,0,0.6);
    border: 1px solid rgba(255,255,255,0.06);
    z-index:4;
  }
  .center-lens h1{margin:0;font-size:18px;letter-spacing:1px;color:var(--accent)}
  .center-lens .btn{
    margin-top:12px;padding:12px 22px;border-radius:30px;background:linear-gradient(90deg,var(--neon1),var(--neon2));
    box-shadow:0 8px 30px rgba(138,43,226,0.18), 0 6px 14px rgba(0,229,255,0.06);
    border:none;color:#021016;font-weight:700;cursor:pointer;font-size:16px;
    transform: translateZ(20px);
  }
  .center-lens .subtitle{font-size:12px;opacity:0.85;margin-top:6px}

  /* Wheel canvas area */
  #wheelCanvas{
    width:460px;height:460px;border-radius:50%;background:transparent;z-index:2;
  }

  /* needle */
  .needle{
    position:absolute;top:30px;left:calc(50% - 12px);width:24px;height:120px;
    z-index:6;display:flex;align-items:flex-start;justify-content:center;pointer-events:none;
  }
  .needle .tip{
    width:0;height:0;border-left:12px solid transparent;border-right:12px solid transparent;
    border-bottom:28px solid #fffb; filter:drop-shadow(0 6px 16px rgba(255,255,255,0.06));
    transform:translateY(-2px);
  }
  .needle .bar{
    width:6px;height:90px;background:linear-gradient(180deg,#ffffff22,#0000);border-radius:6px;
    margin-top:6px; box-shadow:0 6px 20px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.02);
  }

  /* Right: info panel */
  .panel{
    width:100%;height:520px;border-radius:20px;padding:22px;box-sizing:border-box;
    background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
    border: 1px solid rgba(255,255,255,0.04);
    display:flex;flex-direction:column;gap:14px;
    box-shadow: 0 10px 36px rgba(0,0,0,0.5);
  }
  .panel h2{margin:0;font-size:20px;color:var(--neon1);letter-spacing:0.6px}
  .panel p{margin:0;color:#bfeff8;opacity:0.9}
  .names{
    margin-top:10px;padding:12px;border-radius:12px;background:linear-gradient(180deg,rgba(255,255,255,0.02),transparent);
    display:flex;flex-wrap:wrap;gap:8px;align-items:center;
  }
  .chip{padding:8px 12px;border-radius:12px;background:transparent;border:1px solid rgba(255,255,255,0.04);font-weight:600}
  .log{
    margin-top:8px;padding:12px;border-radius:12px;background:linear-gradient(180deg, rgba(0,0,0,0.12), transparent);
    height:200px;overflow:auto;font-size:13px;color:#dffaff;border:1px dashed rgba(255,255,255,0.03);
  }

  /* winner modal */
  .winner-modal{
    position:fixed;inset:0;display:flex;align-items:center;justify-content:center;z-index:9999;
    pointer-events:none; opacity:0; transition:opacity .28s;
  }
  .winner-modal.show{pointer-events:auto;opacity:1}
  .winner-card{
    width:680px;max-width:92vw;border-radius:18px;padding:26px 30px;
    background: linear-gradient(90deg, rgba(10,10,20,0.9), rgba(14,6,30,0.6));
    border: 1px solid rgba(255,255,255,0.06);
    box-shadow: 0 30px 80px rgba(0,0,0,0.6), 0 6px 40px rgba(138,43,226,0.08);
    transform: translateY(-20px) scale(.98); transition: transform .36s;
    display:flex;gap:20px;align-items:center;
  }
  .winner-modal.show .winner-card{transform: translateY(0) scale(1);}
  .winner-avatar{
    width:150px;height:150px;border-radius:50%;display:flex;align-items:center;justify-content:center;
    background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.04), rgba(255,255,255,0.01));
    font-size:40px;font-weight:700;color:var(--neon1);border: 1px solid rgba(255,255,255,0.04);
    box-shadow: 0 10px 40px rgba(138,43,226,0.06), inset 0 2px 8px rgba(255,255,255,0.02);
  }
  .winner-info h3{margin:0;font-size:28px;color:var(--neon2)}
  .winner-info p{margin:6px 0 0 0;color:#cfefff}
  .close-btn{margin-top:16px;padding:10px 16px;border-radius:10px;background:linear-gradient(90deg,var(--neon1),var(--neon2));border:none;color:#021016;font-weight:700;cursor:pointer}

  /* confetti canvas overlay */
  canvas.overlay{position:fixed;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:800}

  /* small footer */
  .footer{margin-top:auto;display:flex;justify-content:space-between;align-items:center;font-size:12px;color:#9fd9e6;opacity:0.9}
  .brand{display:flex;gap:8px;align-items:center}

  /* responsive */
  @media (max-width:980px){
    .wrap{grid-template-columns: 1fr;grid-template-rows:auto auto;row-gap:18px;height:auto;padding:16px}
    .wheel-card{width:88vw;height:88vw}
    .panel{height:auto}
  }
</style>
</head>
<body>
<div class="wrap">
  <!-- Left: wheel -->
  <div class="wheel-card">
    <canvas id="wheelCanvas" width="920" height="920"></canvas>
    <div class="needle" aria-hidden="true">
      <div class="tip"></div>
      <div class="bar"></div>
    </div>

    <div class="center-lens" id="controlLens">
      <h1>下周技术分享抽签</h1>
      <div class="subtitle">抽到的人负责下周分享</div>
      <button class="btn" id="spinBtn">开始抽签</button>
    </div>
  </div>

  <!-- Right: info -->
  <div class="panel">
    <h2>候选名单</h2>
    <p>每次点击“开始抽签” — 转盘会滚动并随机停止。支持重复抽取（可多次运行）。</p>
    <div class="names" id="nameList"></div>

    <div style="display:flex;gap:10px;margin-top:10px;">
      <button id="shuffleBtn" class="btn" style="background:linear-gradient(90deg,#ff9a00,#ff6a6a);">洗牌（随机重排）</button>
      <button id="resetBtn" class="btn" style="background:linear-gradient(90deg,#8aff6a,#00d4ff);">重置日志</button>
    </div>

    <div class="log" id="logArea" aria-live="polite"></div>

    <div class="footer">
      <div class="brand">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" style="filter:drop-shadow(0 6px 16px rgba(0,0,0,0.6))">
          <circle cx="12" cy="12" r="10" stroke="url(#g1)" stroke-width="1.6"/>
          <defs><linearGradient id="g1"><stop offset="0" stop-color="#00e5ff"/><stop offset="1" stop-color="#8a2be2"/></linearGradient></defs>
        </svg>
        <div style="font-weight:600">抽签系统</div>
      </div>
      <div>状态：<span id="statusLabel">等待抽签</span></div>
    </div>
  </div>
</div>

<!-- Winner modal -->
<div class="winner-modal" id="winnerModal" role="dialog" aria-hidden="true">
  <div class="winner-card">
    <div class="winner-avatar" id="winnerAvatar">?</div>
    <div class="winner-info">
      <h3 id="winnerName">名字</h3>
      <p id="winnerRole">恭喜！你将负责下周技术分享 🎉</p>
      <button class="close-btn" id="closeWinner">我知道了</button>
    </div>
  </div>
</div>

<!-- overlay canvas for confetti -->
<canvas class="overlay" id="overlayCanvas"></canvas>

<script>
/* =========================
   Configuration & Data
   ========================= */
const NAMES = [
  "陶诚","陈明坤","陈详","陈玉珠","范常见","高彤","韩奎伟","刘康","李延昊","聂祥贵","沈泽华","吴龙"
];

const wheel = {
  canvas: document.getElementById('wheelCanvas'),
  ctx: null,
  center: 460,
  radius: 420,
  names: [...NAMES], // working copy
  colors: [],
  angle: 0, // current rotation angle (rad)
  spinning: false
};

const overlay = document.getElementById('overlayCanvas');
const overlayCtx = overlay.getContext('2d');
resizeOverlay();

const logArea = document.getElementById('logArea');
const nameListEl = document.getElementById('nameList');
const spinBtn = document.getElementById('spinBtn');
const statusLabel = document.getElementById('statusLabel');

let audioCtx = null;

/* =========================
   Helper functions
   ========================= */
function log(msg){
  const t = new Date().toLocaleTimeString();
  logArea.innerHTML = `<div>[${t}] ${msg}</div>` + logArea.innerHTML;
}

/* ensure overlay sized */
function resizeOverlay(){
  overlay.width = innerWidth;
  overlay.height = innerHeight;
}
window.addEventListener('resize', resizeOverlay);

/* init wheel canvas */
(function initWheelCanvas(){
  wheel.ctx = wheel.canvas.getContext('2d');
  wheel.canvas.width = wheel.canvas.height = 920; // high-res for clarity
  generateColors();
  renderWheel();
  renderNameChips();
})();

function generateColors(){
  const base1 = [0,229,255], base2 = [138,43,226];
  const n = wheel.names.length;
  wheel.colors = [];
  for(let i=0;i<n;i++){
    const t = i / n;
    const r = Math.round(base1[0] * (1-t) + base2[0]*t);
    const g = Math.round(base1[1] * (1-t) + base2[1]*t);
    const b = Math.round(base1[2] * (1-t) + base2[2]*t);
    wheel.colors.push(`rgba(${r},${g},${b},0.12)`);
  }
}

/* draw wheel */
function renderWheel(){
  const ctx = wheel.ctx;
  const { center, radius, names, angle } = wheel;
  const n = names.length;
  ctx.clearRect(0,0,wheel.canvas.width,wheel.canvas.height);

  // background glare
  let g = ctx.createRadialGradient(center, center, 60, center, center, radius);
  g.addColorStop(0, 'rgba(255,255,255,0.03)');
  g.addColorStop(1, 'rgba(0,0,0,0.00)');
  ctx.fillStyle = g;
  ctx.beginPath(); ctx.arc(center,center,radius+6,0,Math.PI*2); ctx.fill();

  ctx.save();
  ctx.translate(center,center);
  ctx.rotate(angle);

  const slice = (Math.PI*2)/n;
  for(let i=0;i<n;i++){
    const start = i*slice;
    const end = start+slice;

    // segment background
    ctx.beginPath();
    ctx.moveTo(0,0);
    ctx.arc(0,0,radius,start,end);
    ctx.closePath();
    ctx.fillStyle = wheel.colors[i];
    ctx.fill();

    // border lines
    ctx.strokeStyle = 'rgba(255,255,255,0.03)';
    ctx.lineWidth = 1;
    ctx.stroke();

    // text
    ctx.save();
    ctx.rotate(start + slice/2);
    ctx.translate(radius*0.62, 0);
    ctx.rotate(Math.PI/2);
    drawSegmentText(ctx, wheel.names[i], slice);
    ctx.restore();
  }
  ctx.restore();

  // outer ring
  ctx.beginPath(); ctx.arc(center,center,radius+8,0,Math.PI*2); ctx.lineWidth=12;
  const lg = ctx.createLinearGradient(center-radius,center,center+radius,center);
  lg.addColorStop(0,'rgba(0,229,255,0.12)'); lg.addColorStop(1,'rgba(138,43,226,0.12)');
  ctx.strokeStyle = lg; ctx.stroke();
}

/* draw text inside each segment with arc-friendly layout */
function drawSegmentText(ctx, text, sliceAngle){
  ctx.fillStyle = 'rgba(255,255,255,0.92)';
  ctx.font = '18px "Segoe UI", Roboto, Arial';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';

  // split into two lines if name long
  const maxWidth = 160;
  const measure = ctx.measureText(text).width;
  if(measure < maxWidth){
    ctx.fillText(text, 0, 0);
  } else {
    // break roughly in middle
    const mid = Math.floor(text.length/2);
    const a = text.slice(0,mid), b = text.slice(mid);
    ctx.fillText(a, 0, -10);
    ctx.fillText(b, 0, 12);
  }
}

/* render name chips on right panel */
function renderNameChips(){
  nameListEl.innerHTML = '';
  wheel.names.forEach(n=>{
    const el = document.createElement('div'); el.className='chip'; el.textContent=n;
    nameListEl.appendChild(el);
  });
}

/* shuffle names */
function shuffleNames(){
  for(let i=wheel.names.length-1;i>0;i--){
    const j = Math.floor(Math.random()*(i+1));
    [wheel.names[i], wheel.names[j]] = [wheel.names[j], wheel.names[i]];
  }
  renderNameChips();
  generateColors();
  renderWheel();
  log('名单已随机重排。');
}

/* =========================
   Spin physics & audio
   ========================= */
function ensureAudio(){
  if(!audioCtx){
    audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  }
}

/* drumroll while spinning */
let drumrollOsc = null;
function startDrumroll(){
  if(!audioCtx) ensureAudio();
  const now = audioCtx.currentTime;
  const o = audioCtx.createOscillator();
  const g = audioCtx.createGain();
  o.type = 'sawtooth';
  o.frequency.setValueAtTime(80, now);
  o.frequency.exponentialRampToValueAtTime(600, now + 3.5);
  g.gain.setValueAtTime(0.0001, now);
  g.gain.exponentialRampToValueAtTime(0.25, now + 0.02);
  g.gain.exponentialRampToValueAtTime(0.0001, now + 3.2);

  o.connect(g); g.connect(audioCtx.destination);
  o.start();
  drumrollOsc = o;
}

/* stop drumroll gently */
function stopDrumroll(){
  if(drumrollOsc){
    try{ drumrollOsc.stop(audioCtx.currentTime + 0.03); }catch(e){}
    drumrollOsc = null;
  }
}

/* ding */
function playDing(){
  if(!audioCtx) ensureAudio();
  const now = audioCtx.currentTime;
  const o = audioCtx.createOscillator();
  const g = audioCtx.createGain();
  o.type = 'sine';
  o.frequency.setValueAtTime(800, now);
  o.frequency.exponentialRampToValueAtTime(1100, now + 0.08);
  g.gain.setValueAtTime(0.0001, now);
  g.gain.exponentialRampToValueAtTime(0.3, now + 0.01);
  g.gain.exponentialRampToValueAtTime(0.0001, now + 0.5);
  o.connect(g); g.connect(audioCtx.destination);
  o.start(); o.stop(now + 0.6);
}

/* victory chord */
function playVictory(){
  if(!audioCtx) ensureAudio();
  const now = audioCtx.currentTime;
  const freqs = [440,660,880];
  freqs.forEach((f,i)=>{
    const o = audioCtx.createOscillator();
    const g = audioCtx.createGain();
    o.type = 'triangle';
    o.frequency.setValueAtTime(f, now + i*0.03);
    g.gain.setValueAtTime(0.0001, now);
    g.gain.exponentialRampToValueAtTime(0.25, now + 0.01);
    g.gain.exponentialRampToValueAtTime(0.0001, now + 1.2);
    o.connect(g); g.connect(audioCtx.destination);
    o.start(); o.stop(now + 1.2);
  });
}

/* =========================
   Spin routine (deterministic random)
   ========================= */
function spinOnce(){
  if(wheel.spinning) return;
  wheel.spinning = true;
  spinBtn.disabled = true;
  statusLabel.textContent = '开始转动...';
  log('抽签开始：转盘转动中');

  // Start audio
  ensureAudio();
  startDrumroll();

  // spin parameters (reset every time to avoid slow-down bug)
  const n = wheel.names.length;
  const slice = (Math.PI*2)/n;

  // compute random target index
  const targetIndex = Math.floor(Math.random()*n);
  // choose random offset inside the target segment so outcome is not always center
  const offset = (Math.random() - 0.5) * (slice * 0.6);

  // We want the wheel.angle to end such that pointer (top) points to the chosen segment.
  // Pointer is at angle = -Math.PI/2 (top). wheel.angle rotates the wheel.
  // segment start angle for index i (in wheel coordinates) is i*slice
  // so compute desired wheel angle:
  // desiredAngle + currentRotation (mod 2pi) = - (i*slice + slice/2 + offset)  (so center of segment at top)
  const currentAngle = wheel.angle % (Math.PI*2);
  const segmentCenter = (targetIndex + 0.5) * slice + offset;
  // targetWheelAngle such that after rotation, segmentCenter aligns with -PI/2:
  const alignAngle = -Math.PI/2 - segmentCenter;
  // We'll rotate multiple full turns + land on alignAngle
  const minExtraTurns = 4; // min # full rotations
  const extraTurns = minExtraTurns + Math.floor(Math.random()*3); // 4~6
  const targetAbsolute = alignAngle - currentAngle + extraTurns*(Math.PI*2);

  // spin animation parameters: use easing out cubic with velocity simulation
  const duration = 3800 + Math.random()*1200; // ms
  const start = performance.now();
  const startAngle = wheel.angle;

  // animate
  function animate(now){
    const t = Math.min(1, (now - start)/duration);
    // easeOutCubic
    const ease = 1 - Math.pow(1-t, 3);
    wheel.angle = startAngle + targetAbsolute * ease;
    renderWheel();

    if(t < 1){
      requestAnimationFrame(animate);
    } else {
      // ensure exact landing
      wheel.angle = startAngle + targetAbsolute;
      renderWheel();
      stopDrumroll();
      playDing();

      // small settle wiggle
      setTimeout(()=> {
        playVictory();
        triggerConfetti();
        const winner = wheel.names[(targetIndex % n + n) % n];
        announceWinner(winner);
        wheel.spinning = false;
        spinBtn.disabled = false;
        statusLabel.textContent = '抽签完成';
      }, 220);
    }
  }
  requestAnimationFrame(animate);
}

/* announce winner */
const winnerModal = document.getElementById('winnerModal');
const winnerNameEl = document.getElementById('winnerName');
const winnerAvatar = document.getElementById('winnerAvatar');


/* 修改 announceWinner：加入在线头像显示 */
function announceWinner(name){
  log(`<strong>抽中：${name}</strong> — 负责下周技术分享`);
  winnerNameEl.textContent = name;

  // 使用在线头像GIF
  const img = document.createElement('img');
  img.src = "https://pic.rmb.bdstatic.com/bjh/news/445f0011d29669bd6903c8c76c50a9ce8186.gif";
  img.style.width = "100%";
  img.style.height = "100%";
  img.style.borderRadius = "50%";
  img.style.objectFit = "cover";
  winnerAvatar.innerHTML = ""; // 清空文字
  winnerAvatar.appendChild(img);

  winnerModal.classList.add('show');
  microBurst();
}

/* close modal */
document.getElementById('closeWinner').addEventListener('click', ()=>{
  winnerModal.classList.remove('show');
});

/* shuffle & reset buttons */
document.getElementById('shuffleBtn').addEventListener('click', ()=>{
  shuffleNames();
  statusLabel.textContent = '名单已重排';
});
document.getElementById('resetBtn').addEventListener('click', ()=>{
  logArea.innerHTML = '';
  statusLabel.textContent = '日志已重置';
});

/* attach spin button */
spinBtn.addEventListener('click', ()=>{
  // resume audio context on some browsers (user gesture)
  if(audioCtx && audioCtx.state === 'suspended'){ audioCtx.resume(); }
  spinOnce();
});

/* initial render of chips */
function renderNameChips(){ /* redefined safe version */
  nameListEl.innerHTML = '';
  wheel.names.forEach(n=>{
    const el = document.createElement('div'); el.className='chip'; el.textContent=n;
    nameListEl.appendChild(el);
  });
}

/* =========================
   Confetti & particles
   ========================= */
let confettiParticles = [];

function triggerConfetti(){
  const count = 120;
  for(let i=0;i<count;i++){
    confettiParticles.push({
      x: innerWidth/2 + (Math.random()-0.5)*240,
      y: innerHeight/2 - 60 + (Math.random()-0.5)*120,
      vx: (Math.random()-0.5)*8,
      vy: -6 - Math.random()*6,
      size: 6 + Math.random()*8,
      life: 2000 + Math.random()*1800,
      age: 0,
      color: randomColor()
    });
  }
  // play a crisp burst
  playBurstSound();
  animateConfetti();
}

function randomColor(){
  const palette = ['#00e5ff','#8a2be2','#ffd200','#ff6a6a','#8aff6a','#00d4ff'];
  return palette[Math.floor(Math.random()*palette.length)];
}

function playBurstSound(){
  if(!audioCtx) ensureAudio();
  const now = audioCtx.currentTime;
  const o = audioCtx.createOscillator();
  const g = audioCtx.createGain();
  o.type = 'square';
  o.frequency.setValueAtTime(260, now);
  g.gain.setValueAtTime(0.0001, now);
  g.gain.exponentialRampToValueAtTime(0.3, now + 0.02);
  g.gain.exponentialRampToValueAtTime(0.0001, now + 0.9);
  o.connect(g); g.connect(audioCtx.destination);
  o.start(); o.stop(now + 1.0);
}

let confettiAnimating = false;
function animateConfetti(){
  if(confettiAnimating) return;
  confettiAnimating = true;
  const ctx = overlayCtx;
  const start = performance.now();
  function frame(now){
    const dt = 16;
    ctx.clearRect(0,0,overlay.width,overlay.height);
    for(let i=confettiParticles.length-1;i>=0;i--){
      const p = confettiParticles[i];
      p.age += dt;
      p.vy += 0.12; // gravity
      p.x += p.vx;
      p.y += p.vy;
      p.vx *= 0.995;
      // rotate color stripe
      ctx.save();
      ctx.translate(p.x,p.y);
      ctx.rotate(p.age/150);
      ctx.fillStyle = p.color;
      ctx.fillRect(-p.size/2, -p.size/2, p.size, p.size*0.6);
      ctx.restore();
      if(p.age > p.life || p.y > overlay.height + 50){
        confettiParticles.splice(i,1);
      }
    }
    if(confettiParticles.length>0){
      requestAnimationFrame(frame);
    } else {
      ctx.clearRect(0,0,overlay.width,overlay.height);
      confettiAnimating = false;
    }
  }
  requestAnimationFrame(frame);
}

/* tiny micro burst around modal */
function microBurst(){
  for(let i=0;i<30;i++){
    confettiParticles.push({
      x: innerWidth/2 + (Math.random()-0.5)*140,
      y: innerHeight/2 + (Math.random()-0.5)*60,
      vx: (Math.random()-0.5)*6,
      vy: -3 - Math.random()*4,
      size: 4 + Math.random()*6,
      life: 1000 + Math.random()*1000,
      age: 0,
      color: randomColor()
    });
  }
  animateConfetti();
}

/* =========================
   small animation loop for subtle background motion
   ========================= */
(function subtleLoop(){
  let t0 = performance.now();
  function loop(now){
    const dt = (now - t0)/1000;
    t0 = now;
    // slowly rotate radial glows by changing a CSS variable? For simplicity we'll oscillate center lens shadow
    const lens = document.querySelector('.center-lens');
    const x = Math.sin(now/2400)*6;
    const y = Math.cos(now/2600)*6;
    lens.style.boxShadow = `0 12px 40px rgba(138,43,226,0.09), ${x}px ${y}px 30px rgba(0,229,255,0.02)`;
    requestAnimationFrame(loop);
  }
  requestAnimationFrame(loop);
})();

/* =========================
   Utility & bootstrap logs
   ========================= */
log('系统加载完成。准备就绪。');
statusLabel.textContent = '等待抽签';

/* Accessibility: allow Enter to trigger spin when button focused */
spinBtn.addEventListener('keyup', (e)=>{ if(e.key === 'Enter') spinOnce(); });

</script>
</body>
</html>
